import { Controller, Context } from 'egg'
import { EntityService } from './entity-service'

/**
 * 数据操作controller基类
 */
export class EntityController extends Controller {
  srv: EntityService
  constructor(ctx: Context, serviceOrRepositoryName: string | EntityService) {
    super(ctx)
    // this.logger.info('EntityController.constructor', typeof serviceOrName, serviceOrName)
    if (serviceOrRepositoryName instanceof EntityService) {
      this.srv = serviceOrRepositoryName
    } else if (typeof serviceOrRepositoryName === 'string') {
      let serviceName = serviceOrRepositoryName
      const [firstChar] = serviceName
      serviceName = serviceName.replace(firstChar, firstChar.toLowerCase())
      // this.logger.info(serviceOrName, serviceName)
      this.srv = this.service[serviceName]
      if (!this.srv) {
        // this.logger.info(`EntityController.constructor new '${serviceOrName}' service.`)
        this.srv = new EntityService(ctx, serviceOrRepositoryName)
      }
    }
  }

  async home() {
    this.ctx.body = `hi ${this.srv}.`
  }

  async all() {
    const { ctx } = this
    ctx.body = await this.srv.all(ctx.query)
  }

  async count() {
    const { ctx } = this
    ctx.body = await this.srv.count(ctx.query)
  }

  /**
   * more see http://docs.sequelizejs.com/manual/tutorial/querying.html
   * or http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-findAll
   * @param {*} query - when empty used ctx.query
   * query schema:
   *  where: null or object or json string.
   *  order: null or object or json string, when undefined or null use app.config.settings.querySettings.
   *  offset: null or integer, when undefined or null use app.config.settings.querySettings.
   *  limit: null or integer, when undefined or null use app.config.settings.querySettings.
   *  attributes: null or object or json string.
   *    attributes: ['name'] or attributes: { exclude: ['password'] }
   *  include: null or object or json string.
   *    include: { model: 'users', where: { name: { $like: 'a%' } } }
   */
  async list() {
    const { ctx } = this
    // this.logger.info(ctx.state)
    ctx.body = await this.srv.list(ctx.query)
  }

  async find() {
    const { ctx } = this
    if (!ctx.params.id) throw new Error('need id.')
    ctx.body = await this.srv.find(ctx.params.id)
  }

  async build() {
    const { ctx } = this
    ctx.body = await this.srv.build()
  }

  async create() {
    const { ctx } = this
    if (!ctx.request.body) throw new Error('need body.')
    const created = await this.srv.create(ctx.request.body)
    ctx.status = 201
    ctx.body = created
  }

  async update() {
    const { ctx } = this
    const options = ctx.params.id || ctx.query
    if (!options) throw new Error('need id or query.')
    if (!ctx.request.body) throw new Error('need body.')
    ctx.body = await this.srv.update(options, ctx.request.body)
  }

  async del() {
    const { ctx } = this
    const options = ctx.params.id || ctx.request.body
    if (!options) throw new Error('need id or body.')
    ctx.status = 200
    ctx.body = await this.srv.del(options)
  }

  /**
   * custom method.
   */

  async query() {
    const { ctx } = this
    // this.logger.info(ctx.request.body)
    ctx.body = await this.srv.list(ctx.request.body)
  }

  async patch() {
    const { ctx } = this
    const options = ctx.params.id || ctx.query
    if (!options) throw new Error('need id or query.')
    if (!ctx.request.body) throw new Error('need body.')
    if (ctx.request.body.id && ctx.request.body.id === ctx.params.id) delete ctx.request.body.id
    ctx.body = await this.srv.update(options, ctx.request.body)
  }
}
